package com.transino.bop.context;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.Date;
import java.util.Properties;

import javax.servlet.ServletContext;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.web.context.support.ServletContextResource;

import com.kanas.framework.util.ExceptionHelper;
import com.transino.bop.domain.Catalog;
import com.transino.bop.domain.Category;
import com.transino.bop.domain.Entity;
import com.transino.bop.domain.Property;
import com.transino.bop.domain.PropertyGroup;

import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;

public class ContextLoader {
	public static String WEB_CONTEXT_CONFIG_PARA = "kanasContextConfig";
	public static String DEFAULT_CONFIG_PATH="classpath:kanas.properties";
	private static final String CONFIG_KEY_SCAN_ENTITY = "entity-scan";
	private static String ENTITY_SCAN_CATALOG_FILENAME="catalog.xml";
	
	private WebAppContext context = null;
	private Configuration cfg = null;
	private Properties properties = null;
	
	private static final Logger logger = LoggerFactory.getLogger(ContextLoader.class);
	
	public WebAppContext initWebAppContext(ServletContext servletContext) {
		if(servletContext.getAttribute(WebAppContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
			throw new IllegalStateException(
					"Cannot initialize context because there is already a root application context present - " +
					"check whether you have multiple ContextLoader* definitions in your web.xml!");
		}
		cfg = new Configuration();
		cfg.setObjectWrapper(new DefaultObjectWrapper());
		properties = new Properties();
		context = new WebAppKanasContext(servletContext);
		try {
			String resourcePath = servletContext.getInitParameter(WEB_CONTEXT_CONFIG_PARA);
			if(resourcePath == null) {
				resourcePath = DEFAULT_CONFIG_PATH;
			}
			Resource r = null;
			String[] parts = resourcePath.split(":");
			if(parts.length > 1) {
				if(parts[0].equalsIgnoreCase("classpath")) {
					r = new ClassPathResource(parts[1]);
				}else {
					r = new UrlResource(resourcePath);
				}
			}else {
				r = new ServletContextResource(servletContext, resourcePath);
			}
			if(r != null) {
				properties.load(r.getInputStream());
			}
			if(properties.getProperty(ContextLoader.CONFIG_KEY_SCAN_ENTITY) == null) {
				properties.setProperty(ContextLoader.CONFIG_KEY_SCAN_ENTITY, ContextLoader.CONFIG_KEY_SCAN_ENTITY);
			}
			String scanFolder = properties.getProperty(ContextLoader.CONFIG_KEY_SCAN_ENTITY);
			String tail = scanFolder.substring(scanFolder.length() - 1);
			if(!("\\".equals(tail)||"/".equals(tail))) {
				scanFolder += File.separator;
			}
			Resource r2 = new ServletContextResource(servletContext, scanFolder+ContextLoader.ENTITY_SCAN_CATALOG_FILENAME);
			JAXBContext jc = null;
			Unmarshaller u = null;
			if(r2.exists()) {
				jc = JAXBContext.newInstance(Catalog.class.getPackage().getName());
				u = jc.createUnmarshaller();
				Catalog cata = (Catalog) u.unmarshal(r2.getInputStream());
				if(null != cata) {
					for(Category c:cata.getCategories()) {
						setSuperCategory(c);
					}
				}else {
					logger.error("There is no catalog found, please check the catalog file.");
				}
				this.context.setCatalog(cata);
			}
			if(jc == null) {
				jc = JAXBContext.newInstance(Category.class.getPackage().getName());
				u = jc.createUnmarshaller();
			}
			Resource r3 = new ServletContextResource(servletContext, scanFolder);
			if(r3!=null && r3.getFile().isDirectory()) {
				FileFilter ff = new FileFilter() {
					@Override
					public boolean accept(File pathname) {
						return pathname.isFile();
					}
				};
				Entity e;
				Category c;
				PropertyGroup group;
				for(File f:r3.getFile().listFiles(ff)) {
					if(!f.getName().startsWith("m_"))continue;
					e = (Entity) u.unmarshal(f);
					e.setId(f.getName().split("\\.")[0]);
					c = this.context.getCategory(e.getCategoryId());
					if(null != c) {
						e.setSuperCategory(c);
						c.getEntities().add(e);
					}
					if(null != e.getStrPrimary()) {
						e.setPrimary(e.getStrPrimary().split(","));
					}
					for(PropertyGroup pg:e.getGroups()) {
						pg.setEntity(e);
						this.context.registerGroup(pg);
					}
					for(Property p:e.getProperties()) {
						if(p.getValidator()!="")this.context.modifyValidator(e, p);
						this.context.registerProperty(p);
						p.setEntity(e);
						if(p.getGroupId()!=null && p.getGroupId()!="") {
							group = this.context.getGroup(p.getGroupId());
							p.setGroup(group);
							group.getProperties().add(p);
						}
					}
					this.context.registerEntity(e);
				}
			}
		} catch(IOException e) {
			logger.error("There is an IO exception occured.");
			ExceptionHelper.loggingError(e, logger);
		} catch (JAXBException e) {
			e.printStackTrace();
		}
		this.context.setProperties(properties);
		this.context.setStartDate(new Date());
		servletContext.setAttribute(WebAppContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
		return this.context;
	}
	
	private void setSuperCategory(Category superCategory) {
		Category superCateogry = superCategory;
		for(Category c:superCategory.getSubCategories()) {
			c.setSuperCateogry(superCateogry);
			setSuperCategory(c);
		}
	}
	
	public void closeWebAppContext(ServletContext servletContext) {
		servletContext.removeAttribute(WebAppContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
		this.context = null;
		properties.clear();
		properties = null;
		cfg = null;
	}	
	
}
